/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package researchgrants.parts.LoggedData;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import researchgrants.parts.db.Db;

/**
 * A container to hold a datum and manage its manipulation: getting the latest value, setting a new value, getting previous values.
 * This piece of data can be changed and its changes will be saved in the database for future retreivals.
 * 
 * The security consideration in the HistoricallyLoggedData is not good. One can write a piece of code that can retreive all values stored in the HistoricallyLoggedData tables. However, a user, can't do the same; only a developer. To alleviate such security risk, one can implement a refernce from the HistoricallyLoggedData to the containing object and check its validity upon loading of data, by comparing the container object in the database against a given object: static HistoricallyLoggedData getById(int id) ==> static HistoricallyLoggedData getById(Object owner, int id).
 * @author ohad
 */
public class HistoricallyLoggedData {
    
    // allowing to create a new HistoricallyLoggedData. It must have an initial value sent.
    // calling code can afterward retreive the new id and save it as a reference for the HistoricallyLoggedData and later call getById with the reference to reload the data.
    public static HistoricallyLoggedData createNew(LogableObject initialValue) {
        int newLoggedDataId=-1;
        try {
            Connection conn=Db.openDbConnection();
            
            // Creates a new HistoricallyLoggedData
            PreparedStatement prepareStatement = Db.createPreparedStatement(conn, "INSERT INTO tblLoggedData (LastLoggedDataChangeID) VALUES (-1)");
            prepareStatement.executeUpdate();
            newLoggedDataId = Db.getLastIdentity();
            prepareStatement.close();
            
            // Creates the initial value
            PreparedStatement newLoggedDataChangeStatement = Db.createPreparedStatement(conn, "INSERT INTO tblLoggedDataChanges (LoggedDataID, [Value]) VALUES (?, ?)");
            newLoggedDataChangeStatement.setInt(1, newLoggedDataId);
            newLoggedDataChangeStatement.setString(2, LogableObjectsManager.logableObjectToLoggedString(initialValue));
            newLoggedDataChangeStatement.executeUpdate();
            int newLoggedDataChangeId=Db.getLastIdentity();
            newLoggedDataChangeStatement.close();
            
            // Updates the current value to the new change
            PreparedStatement updateLoggedDataStatement=Db.createPreparedStatement(conn, "UPDATE tblLoggedData SET LastLoggedDataChangeID=? WHERE LoggedDataID=?");
            updateLoggedDataStatement.setInt(1, newLoggedDataChangeId);
            updateLoggedDataStatement.setInt(2, newLoggedDataId);
            updateLoggedDataStatement.executeUpdate();
            updateLoggedDataStatement.close();
            
            Db.closeDbConnection(conn);
        } catch (SQLException ex) {
            Logger.getLogger(HistoricallyLoggedData.class.getName()).log(Level.SEVERE, null, ex);
        }
        return(getById(newLoggedDataId));
    }
    
    // a factory that queries the database about the logged data
    public static HistoricallyLoggedData getById(int id) throws IllegalArgumentException {
        return (new HistoricallyLoggedData(id));
    }

    // late initialization of the actual value
    private HistoricallyLoggedData(int id)  throws IllegalArgumentException {
        this.id = id;
//        syncDataFromDb();
    }
    
    private int id;
    
    private String currentValue;

    
    // returns the unique identifier of the HistoricallyLoggedData. can be called to get a reference for future retrieval
    public int getId() {
        return(id);
    }

    // populate the data from the database
    private void syncDataFromDb() {
        try {
            Connection conn=Db.openDbConnection();
            // selects the last inserted value
            PreparedStatement stat = Db.createPreparedStatement(conn, "SELECT Value FROM TblLoggedData INNER JOIN TblLoggedDataChanges ON TblLoggedData.LastLoggedDataChangeID=TblLoggedDataChanges.LoggedDataChangeID Where TblLoggedData.LoggedDataID=?");
            stat.setInt(1, id);
            stat.executeQuery();
            ResultSet resultSet=stat.getResultSet();
            currentValue = (String) Db.getSingleRowSingleColumn(resultSet);
            resultSet.close();
            stat.close();
            Db.closeDbConnection(conn);
        } catch (SQLException ex) {
            Logger.getLogger(HistoricallyLoggedData.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            throw new RuntimeException("Problem with Data: " + ex.getLocalizedMessage());
        }
    }
    
    /**
     * returns the latest value saved
     * @return the latest value saved for this logged data
     */
    public LogableObject value () {
        syncDataFromDb();
        return(LogableObjectsManager.loggedStringToLogableObject(currentValue));
    }

    public void setNewValue(LogableObject value) {
        try {
            Connection conn=Db.openDbConnection();
            
            PreparedStatement insertChangeStatement = Db.createPreparedStatement(conn, "INSERT INTO tblLoggedDataChanges (LoggedDataID, [Value]) VALUES (?,?)");
            insertChangeStatement.setInt(1, id);
            insertChangeStatement.setString(2, LogableObjectsManager.logableObjectToLoggedString(value));
            insertChangeStatement.executeUpdate();
            int newLoggedDataChangeID = Db.getLastIdentity();
            insertChangeStatement.close();
            
            PreparedStatement updateLoggedDataAboutChangeStatement= Db.createPreparedStatement(conn, "UPDATE TblLoggedData SET LastLoggedDataChangeID=? WHERE LoggedDataID=?");
            updateLoggedDataAboutChangeStatement.setInt(1, newLoggedDataChangeID);
            updateLoggedDataAboutChangeStatement.setInt(2, id);
            updateLoggedDataAboutChangeStatement.executeUpdate();

            Db.closeDbConnection(conn);
        } catch (SQLException ex) {
            ex.getSQLState();
            ex.getMessage();
            
            Logger.getLogger(HistoricallyLoggedData.class.getName()).log(Level.SEVERE, "Problem updating the new value", ex);
        }
        syncDataFromDb();
    }
    
//    public LoggedDataChange[] getAllChanges() {
//        return(null);
//    }
//    
//   public class LoggedDataChange {
//        String value;
//        Date chagneDate;
//    }
}
